%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%       tcp监听器
%%% @end
%%% Created : 19. 6月 2021 11:52
%%%-------------------------------------------------------------------
-module(tcp_acceptor).
-author("liuwentao").

-behaviour(gen_server).


%% 进程接口
-export([
    start_link/1        %% 进程开启函数
]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-include("common.hrl").

%%%===================================================================
%%% 工具函数
%%%===================================================================
%% flash跨域策略文件内容
-define(FL_POLICY_FILE,         <<"<cross-domain-policy><allow-access-from domain='*' to-ports='*' /></cross-domain-policy>">>).

%% 监控服务器握手消息
-define(CLIENT_MONITOR,     <<"monitor---------">>).
%% WEB端通讯消息
-define(WEB_CONN,           <<"web_conn---------------">>).
%% 测试器握手消息
-define(CLIENT_TESTER,      <<"tester----------">>).
%% 游戏客户端握手消息
-define(CLIENT_GAME,        <<"game_client------------">>).
%% flash策略文件请求
-define(CLIENT_FL_POLICY_REQ,   <<"<policy-file-request/>\0">>).

%% 连接握手超时在DEBUG模式为2分钟，正常是10秒
-ifdef(debug).
-define(TIMEOUT_HANDSHAKE, 120000).
-else.
-define(TIMEOUT_HANDSHAKE, 10000).
-endif.

%%----------------------------------------------------
%% 对外接口
%%----------------------------------------------------

%% @doc 启动acceptor
-spec start_link(port()) -> ignore | {ok, pid()} | {error, term()}.
start_link(LSock) ->
    gen_server:start_link(?MODULE, [LSock], []).

%%----------------------------------------------------
%% 内部处理
%%----------------------------------------------------

init([LSock]) ->
    ?INFO("[~w] 已启动...", [?MODULE]),
    self() ! loop,
    {ok, {LSock}}.

handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(loop, State = {LSock}) ->
    case gen_tcp:accept(LSock) of
        {ok, Socket} ->
            {ok, {_, Port}} = inet:peername(Socket),
            ?INFO("self[~p] 接受连接Socket[~p][~p]", [self(), Socket, Port]),
            gen_tcp:controlling_process(Socket, spawn(fun() -> accept(Socket) end));
        {error, closed} -> ignore;
        {error, Reason} ->
            ?ERR("接受socket连接时发生了未预料的错误:~w", [Reason])
    end,
    self() ! loop, %% 继续等待下一个
    {noreply, State};

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%----------------------------------------------------
%% 私有函数
%%----------------------------------------------------

%% 接受一个连接
accept(Socket) ->
    case gen_tcp:recv(Socket, 23, ?TIMEOUT_HANDSHAKE) of
        {ok, ?CLIENT_FL_POLICY_REQ} ->
            ?DEBUG("接受一个连接"),
            gen_tcp:send(Socket, ?FL_POLICY_FILE),
            gen_tcp:close(Socket);

        {ok, ?WEB_CONN} ->
            ?DEBUG("接受一个连接"),
            web_conn:handle(Socket);

        {ok, ?CLIENT_MONITOR} ->
            ?DEBUG("接受一个连接"),
            create_conn(monitor, 4, Socket);

        {ok, ?CLIENT_GAME} ->
            ?DEBUG("接受一个连接"),
            create_conn(game, 4, Socket);

        {ok, ?CLIENT_TESTER} ->
            ?DEBUG("接受一个连接"),
            create_conn(tester, 4, Socket);

        {error, closed} ->
            ?DEBUG("收到关闭socket消息"),
            gen_tcp:close(Socket);

        {error, timeout} ->
            ?DEBUG("建立连接失败:~w", [{error, timeout}]);

        _Else ->
            ?DEBUG("建立连接失败:~w", [_Else]),
            {ok, {Ip, _Port}} = inet:peername(Socket),
            ?DEBUG("[~p]建立连接失败:~w", [Ip, _Else]),
            gen_tcp:close(Socket)
    end.

%% 创建连接进程
create_conn(ClientType, _Packet, Socket) ->
    try
        {ok, {Ip, Port}} = inet:peername(Socket),
        {ok, Pid} = server_conn:create(ClientType, Socket, Ip, Port),
        gen_tcp:controlling_process(Socket, Pid)
    catch
        T:X ->
            ?ERR("建立连接失败[~w : ~w]", [T, X]),
            gen_tcp:close(Socket)
    end.
